/*
 * Copyright 2003-2007 (c) Live Media Pty Ltd. <argot@einet.com.au> 
 *
 * This software is licensed under the Argot Public License 
 * which may be found in the file LICENSE distributed 
 * with this software.
 *
 * More information about this license can be found at
 * http://www.einet.com.au/License
 * 
 * The Developer of this software is Live Media Pty Ltd,
 * PO Box 4591, Melbourne 3001, Australia.  The license is subject 
 * to the law of Victoria, Australia, and subject to exclusive 
 * jurisdiction of the Victorian courts.
 */

package com.colony.zone.process;

import java.net.URISyntaxException;
import java.util.HashMap;

import com.colony.concurrent.ThreadPool;
import com.colony.zone.CRL;
import com.colony.zone.IBoundZoneObject;
import com.colony.zone.IMessageReceiverObject;
import com.colony.zone.IRealm;
import com.colony.zone.IZone;
import com.colony.zone.ZoneException;

public class HashMapZone 
implements IZone
{
	private CRL _location;
	private IZone _parent;
	private IRealm _realm;
	private ThreadPool _threadPool;
	private boolean _isBound = false;
    private HashMap  _bindings = new HashMap();
    private static final String empty = new String("empty");
	
    private class HashtableBinding
    {
       public Object value;
    }

	public void bind(CRL location, IZone parent) 
	throws ZoneException 
	{
		_location = location;
		_parent = parent;
		if (parent!=null)
		{
			_threadPool = parent.getThreadPool();
			_realm = _parent.getRealm();
		}
		_isBound = true;
	}
	
	public boolean isBound()
	{
		return _isBound;
	}
	
	private void checkBound()
	throws ZoneException
	{
		if (!isBound())
			throw new ZoneException("Zone not bound to parent");
	}
	
	public CRL getLocation() 
	throws ZoneException 
	{
		checkBound();
		return _location;
	}

	public IRealm getRealm() 
	throws ZoneException 
	{
		checkBound();
		return _realm;
	}
	
	public ThreadPool getThreadPool()
	throws ZoneException
	{
		return _threadPool;
	}
	
	public String getName() 
	throws ZoneException 
	{
		checkBound();
		return _location.getName();
	}

	public void addZone(String name, IZone zone) 
	throws ZoneException 
	{
		checkBound();
    	Object o = _bindings.get( name );
    	if ( null != o ) throw new ZoneException( "Object name already in use." );

    	try 
    	{
    		put( name, zone);
			zone.bind( new CRL( _location, name), this);
		} 
    	catch (URISyntaxException e) 
		{
			throw new ZoneException( e.getMessage(), e );
		}
	}


	public Object get(String name) 
	throws ZoneException 
	{
		checkBound();
		if ("".equals(name))
			return this;
		Object o = _bindings.get(name);
		if (null == o)
			return o;
		HashtableBinding hb = (HashtableBinding) o;
		if (hb.value == empty)
			return null;
		return hb.value;
	}


	public void put(String name, Object data) 
	throws ZoneException 
	{
		checkBound();
    	Object o = _bindings.get(name);
		if (null != o)
			throw new ZoneException("Object name already in use.");

		if ( data instanceof IBoundZoneObject)
		{
			IBoundZoneObject bound = (IBoundZoneObject) data;
	    	try 
	    	{
				bound.bind( new CRL( _location, name), this);
			} 
	    	catch (URISyntaxException e) 
			{
				throw new ZoneException( e.getMessage(), e );
			}			
		}
		
		HashtableBinding hb = new HashtableBinding();
		hb.value = data;
		_bindings.put(name, hb);		
	}

	public Object remove(String name) 
	throws ZoneException 
	{
		checkBound();
	   	Object o = _bindings.remove(name);
		if (null == o)
			return null;
		HashtableBinding hb = (HashtableBinding) o;
		return hb.value;
    }

    public void rename( String name, String newName )
    throws ZoneException
    {
    	checkBound();
        Object o = _bindings.get(name);
		if (null == o)
			throw new ZoneException("Can not rename, object does not exist");
		Object o2 = _bindings.get(newName);
		if (o2 != null)
			throw new ZoneException(
					"Can not rename, object name exists already");
		_bindings.remove(name);
		_bindings.put(newName, o);
    	
    }

	public Object find(CRL location) 
	throws ZoneException 
	{
		checkBound();
		if (_location.isSameBase(location))
		{
			IZone zone = this;
			int localPathPos = _location.getPathSize();
			int locationPathSize = location.getPathSize();
			if (localPathPos == locationPathSize)
			{
				return this;
			}
			for (int x=localPathPos;x<locationPathSize;x++)
			{
				String part = location.getPathPart(localPathPos);				
				if (part==null) return this;
				Object o = zone.get(part);
				if (o instanceof IZone && locationPathSize > localPathPos+1 )
				{
					zone = (IZone) o;
				}
				else if (o instanceof IRealm)
				{
					return ((IRealm)o).find(location);
				}
				return o;
			}
		}
		if (_realm==null)
		{
			throw new ZoneException("Can't find "+location.toString());
		}		
		return _realm.find(location);
	}	
    
	public void send(CRL location, Object object) 
	throws ZoneException 
	{
		checkBound();
		if (_location.isSameBase(location))
		{
			IZone zone = this;
			int localPathPos = _location.getPathSize();
			int locationPathSize = location.getPathSize();
			for (int x=localPathPos;x<locationPathSize;x++)
			{
				String part = location.getPathPart(x);				
				if (part==null) 
					throw new ZoneException("Unable to send object to "+location.toString());
				Object o = zone.get(part);
				if (o instanceof IZone && locationPathSize > localPathPos+1 )
				{
					zone = (IZone) o;
				}
				else if (o instanceof IRealm)
				{
					((IRealm)o).send(location,object);
					return;
				}
				else if (o instanceof IMessageReceiverObject && locationPathSize > localPathPos+1)
				{
					((IMessageReceiverObject)o).send(object);
					return;
				}
				else
				{
					throw new ZoneException("Unable to send object to "+location.toString());
				}
			}			
		}
		if (_realm==null)
		{
			throw new ZoneException("Can't send to "+location.toString());
		}
		_realm.send(location,object);
	}




}
